module apb_sram #(
    parameter REG_ADDR    = 24'b000000, 
    parameter ADDR_BITS   = 16,
    parameter DATA_BITS   = 8,
    parameter WAIT_CYCLES = 3
)(
    input  wire                 pclk,                   // APB bus clock.
    input  wire                 presetn,                // APB reset
    input  wire                 psel,                   // APB peripheral select.
    input  wire                 penable,                // APB enable signal, indicates ACCESS phase.
    input  wire                 pwrite,                 // APB write enable, 1 = write.
    input  wire [31:0]          paddr,                  // APB address bus.
    input  wire [31:0]          pwdata,                 // APB write data bus.
    output reg  [31:0]          prdata,                 // APB read data bus.
    output reg                  pready,                 // APB ready signal, indicates transaction complete.

    input  wire [31:0]          access_addr,            // Memory cell address.
    output wire                 access_done_pulse,      // Memory cell access done pulse.

    output reg  [ADDR_BITS-1:0] sram_addr,              // SRAM address bus.
    inout  wire [DATA_BITS-1:0] sram_dq,                // SRAM bidirectional data bus.
    output reg                  sram_cs_n,              // SRAM chip select.
    output reg                  sram_we_n,              // SRAM write enabled.
    output reg                  sram_oe_n               // SRAM read enabled.
);

// Shift register for pready's rising edge detection.
reg [1:0]           pready_shift;

always @(posedge pclk or negedge presetn) begin
    if (~presetn)
        pready_shift <= 2'b11;
    else
        pready_shift <= {pready_shift[0], pready};
end

// Output a single-cycle pulse when a rising edge is detected.
assign access_done_pulse = (pready_shift == 2'b01);

// SRAM tri-state control.
reg [DATA_BITS-1:0] sram_out;
reg                 sram_out_oe;
assign              sram_dq = sram_out_oe ? sram_out : {DATA_BITS{1'bz}};

// Wait cycle counter.
reg [15:0] wait_cnt;

always@(posedge pclk or negedge presetn) begin
    if(~presetn) begin
        sram_cs_n    <= 1;
        sram_we_n    <= 1;
        sram_oe_n    <= 1;
        sram_out_oe  <= 0;
        prdata       <= 0;
        pready       <= 1;
        wait_cnt     <= 0;
    end 
    else begin
        if(psel && paddr[23:0] == REG_ADDR) begin
            // SETUP phase
            if(~penable) begin
                sram_addr <= access_addr[ADDR_BITS-1:0];
                sram_cs_n <= 0;
                if(pwrite) begin
                    sram_we_n   <= 0;
                    sram_oe_n   <= 1;
                    sram_out    <= pwdata[DATA_BITS-1:0]; // Data written to SRAM.
                    sram_out_oe <= 1;
                end else begin
                    sram_we_n   <= 1;
                    sram_oe_n   <= 0;
                    sram_out_oe <= 0;
                end
                wait_cnt <= 0;
                pready   <= 0;
            end
            // ACCESS phase
            else begin
                if(wait_cnt < WAIT_CYCLES) begin
                    wait_cnt <= wait_cnt + 1;
                    pready   <= 0; // Wait cycles.
                end 
                else begin
                    if(~pwrite) begin
                        prdata  <= {{(32-DATA_BITS){1'b0}}, sram_dq}; // Read data from SRAM.
                    end
                    wait_cnt    <= 0;
                    pready      <= 1; // Access complete.
                    sram_cs_n   <= 1;
                    sram_we_n   <= 1;
                    sram_oe_n   <= 1;
                    sram_out_oe <= 0;
                    
                end
            end
        end 
        else begin
            // Non-Access cycles.
            sram_cs_n    <= 1;
            sram_we_n    <= 1;
            sram_oe_n    <= 1;
            sram_out_oe  <= 0;
            pready       <= 1;
            wait_cnt     <= 0;
        end
    end
end
endmodule